// Copyright Epic Games, Inc. All Rights Reserved.

/**
 *	
 *	===================== LyraReplicationGraph Replication =====================
 *
 *	概述
 *	
 *		这改变了角色相关性的工作方式。AActor::IsNetRelevantFor 在这个系统中不被使用！
 *		
 *		相反，ULyraReplicationGraph 包含 UReplicationGraphNodes。这些节点负责为每个连接生成要复制的角色列表。
 *		这些列表中的大多数在帧之间是持久存在的。这使得大部分的收集工作（"哪些角色应该被考虑进行复制"）可以被共享/重用。
 *		节点可以是全局的（被所有连接使用）、连接特定的（每个连接获得自己的节点）或共享的（例如，团队：同一团队上的所有连接共享）。
 *		角色可以在多个节点中！例如，一个Pawn可能位于空间化节点中，但也位于始终相关团队节点中。对于队友，它将被返回两次。
 *		这是可以的，但应尽可能最小化。
 *		
 *		ULyraReplicationGraph 旨在不被游戏代码直接使用。也就是说，您不应该在任何其他地方包含 LyraReplicationGraph.h。
 *		相反，ULyraReplicationGraph 依赖于游戏代码，并注册游戏代码广播的事件（例如，玩家加入/离开团队的事件）。
 *		做出这个选择是因为它给了 ULyraReplicationGraph 一个完整的整体视角来看待角色复制。而不是暴露任何游戏代码地方都可以调用的通用公共函数，
 *		所有通知都明确注册在 ULyraReplicationGraph::InitGlobalActorClassSettings 中。
 *		
 *	Lyra 节点
 *	
 *		这些是当前使用的顶级节点：
 *		
 *		UReplicationGraphNode_GridSpatialization2D: 
 *		这是空间化节点。所有"基于距离相关"的角色将被路由到这里。这个节点将地图划分为一个2D网格。网格中的每个单元格包含
 *		基于它们如何更新/进入休眠的子节点，这些子节点持有角色列表。角色被放在多个单元格中。连接从它们所在的单个单元格中拉取。
 *		
 *		UReplicationGraphNode_ActorList
 *		这是一个包含始终相关角色的角色列表节点。这些角色始终与每个连接相关。
 *		
 *		ULyraReplicationGraphNode_AlwaysRelevant_ForConnection
 *		这是连接特定始终相关角色的节点。这个节点不维护持久列表，而是每帧构建它。这是可能的，因为（目前）
 *		这些角色都可以从 PlayerController 轻松访问。持久列表需要在角色更改时广播通知，这将是可能的
 *		但目前不是必需的。
 *		
 *		ULyraReplicationGraphNode_PlayerStateFrequencyLimiter
 *		一个用于处理玩家状态复制的自定义节点。这复制一小部分滚动设置的玩家状态（目前为2/帧）。这是为了让玩家状态以低、
 *		稳定的频率复制到模拟连接，并利用序列化共享。自动代理玩家状态以更高的频率复制（仅对
 *		拥有连接）通过 ULyraReplicationGraphNode_AlwaysRelevant_ForConnection。
 *		
 *		UReplicationGraphNode_TearOff_ForConnection
 *		用于处理拆卸角色的连接特定节点。这是在复制图的基础实现中创建和管理的。
 *	
 *	如何使用
 *	
 *		使某些东西始终相关：如果可以，请避免这样做 :) 如果必须，只需在类默认值中设置 AActor::bAlwaysRelevant = true 即可。
 *		
 *		使某些东西对连接始终相关：您需要修改 ULyraReplicationGraphNode_AlwaysRelevant_ForConnection::GatherActorListsForConnection。您还会想要
 *		确保角色不会被放入其他节点之一。最安全的方法是通过在 ULyraReplicationGraph::InitGlobalActorClassSettings 中设置其 EClassRepNodeMapping 为 NotRouted。
 *
 *	如何调试
 *	
 *		禁用复制图以查看您的问题是否特定于此系统或只是一般的复制/游戏玩法问题是一个好主意。
 *		
 *		如果是复制图相关的问题，有几个有用的命令可以使用：参见 ReplicationGraph_Debugging.cpp。最有用的如下。使用 'cheat' 命令从客户端在服务器上运行这些命令。
 *	
 *		"Net.RepGraph.PrintGraph" - 这将把图打印到日志中：每个节点和角色。
 *		"Net.RepGraph.PrintGraph class" - 同上，但将按类分组。
 *		"Net.RepGraph.PrintGraph nclass" - 同上，但将按本地类分组（隐藏蓝图噪音）
 *		
 *		Net.RepGraph.PrintAll <Frames> <ConnectionIdx> <"Class"/"Nclass"> - 将打印整个图、收集的角色以及它们在给定连接的X帧内如何被优先处理。
 *		
 *		Net.RepGraph.PrintAllActorInfo <ActorMatchString> - 将打印与角色/类关联的类、全局和连接复制信息。如果 MatchString 为空，将打印所有内容。直接从客户端调用。
 *		
 *		Lyra.RepGraph.PrintRouting - 将打印每个类的 EClassRepNodeMapping。也就是说，给定的角色类如何被路由（或不路由）在复制图中。
 *	
 */

#include "LyraReplicationGraph.h"

#include "Net/UnrealNetwork.h"
#include "Engine/LevelStreaming.h"
#include "EngineUtils.h"
#include "CoreGlobals.h"

#if WITH_GAMEPLAY_DEBUGGER
#include "GameplayDebuggerCategoryReplicator.h"
#endif

#include "GameFramework/GameModeBase.h"
#include "GameFramework/GameState.h"
#include "GameFramework/PlayerState.h"
#include "GameFramework/Pawn.h"
#include "Engine/LevelScriptActor.h"
#include "Engine/NetConnection.h"
#include "UObject/UObjectIterator.h"

#include "LyraReplicationGraphSettings.h"
#include "Character/LyraCharacter.h"
#include "Player/LyraPlayerController.h"

DEFINE_LOG_CATEGORY( LogLyraRepGraph ); // 定义 Lyra 复制图日志类别

namespace Lyra::RepGraph
{
	float DestructionInfoMaxDist = 30000.f; // 销毁信息最大距离
	static FAutoConsoleVariableRef CVarLyraRepGraphDestructMaxDist(TEXT("Lyra.RepGraph.DestructInfo.MaxDist"), DestructionInfoMaxDist, TEXT("复制销毁信息的最大距离（非平方）"), ECVF_Default); // 控制台变量：销毁信息最大距离

	int32 DisplayClientLevelStreaming = 0; // 显示客户端关卡流
	static FAutoConsoleVariableRef CVarLyraRepGraphDisplayClientLevelStreaming(TEXT("Lyra.RepGraph.DisplayClientLevelStreaming"), DisplayClientLevelStreaming, TEXT(""), ECVF_Default); // 控制台变量：显示客户端关卡流

	float CellSize = 10000.f; // 单元格大小
	static FAutoConsoleVariableRef CVarLyraRepGraphCellSize(TEXT("Lyra.RepGraph.CellSize"), CellSize, TEXT(""), ECVF_Default); // 控制台变量：单元格大小

	// 本质上是复制的"最小 X"。这只是一个初始值。如果角色出现在此之外，系统将自行重置。
	float SpatialBiasX = -150000.f; // 空间偏置 X
	static FAutoConsoleVariableRef CVarLyraRepGraphSpatialBiasX(TEXT("Lyra.RepGraph.SpatialBiasX"), SpatialBiasX, TEXT(""), ECVF_Default); // 控制台变量：空间偏置 X

	// 本质上是复制的"最小 Y"。这只是一个初始值。如果角色出现在此之外，系统将自行重置。
	float SpatialBiasY = -200000.f; // 空间偏置 Y
	static FAutoConsoleVariableRef CVarLyraRepSpatialBiasY(TEXT("Lyra.RepGraph.SpatialBiasY"), SpatialBiasY, TEXT(""), ECVF_Default); // 控制台变量：空间偏置 Y

	// 将动态、空间化的角色分布到多少个桶中。高数字 = 更多桶 = 更小的有效复制频率。这发生在各个角色进行自己的 NetUpdateFrequency 检查之前。
	int32 DynamicActorFrequencyBuckets = 3; // 动态角色频率桶
	static FAutoConsoleVariableRef CVarLyraRepDynamicActorFrequencyBuckets(TEXT("Lyra.RepGraph.DynamicActorFrequencyBuckets"), DynamicActorFrequencyBuckets, TEXT(""), ECVF_Default); // 控制台变量：动态角色频率桶

	int32 DisableSpatialRebuilds = 1; // 禁用空间重建
	static FAutoConsoleVariableRef CVarLyraRepDisableSpatialRebuilds(TEXT("Lyra.RepGraph.DisableSpatialRebuilds"), DisableSpatialRebuilds, TEXT(""), ECVF_Default); // 控制台变量：禁用空间重建

	int32 LogLazyInitClasses = 0; // 记录延迟初始化类
	static FAutoConsoleVariableRef CVarLyraRepLogLazyInitClasses(TEXT("Lyra.RepGraph.LogLazyInitClasses"), LogLazyInitClasses, TEXT(""), ECVF_Default); // 控制台变量：记录延迟初始化类

	// 为快速共享移动更新使用多少带宽。这是独立于 NetDriver 的目标带宽进行计数的。
	int32 TargetKBytesSecFastSharedPath = 10; // 快速共享路径目标千字节/秒
	static FAutoConsoleVariableRef CVarLyraRepTargetKBytesSecFastSharedPath(TEXT("Lyra.RepGraph.TargetKBytesSecFastSharedPath"), TargetKBytesSecFastSharedPath, TEXT(""), ECVF_Default); // 控制台变量：快速共享路径目标千字节/秒

	float FastSharedPathCullDistPct = 0.80f; // 快速共享路径剔除距离百分比
	static FAutoConsoleVariableRef CVarLyraRepFastSharedPathCullDistPct(TEXT("Lyra.RepGraph.FastSharedPathCullDistPct"), FastSharedPathCullDistPct, TEXT(""), ECVF_Default); // 控制台变量：快速共享路径剔除距离百分比

	int32 EnableFastSharedPath = 1; // 启用快速共享路径
	static FAutoConsoleVariableRef CVarLyraRepEnableFastSharedPath(TEXT("Lyra.RepGraph.EnableFastSharedPath"), EnableFastSharedPath, TEXT(""), ECVF_Default); // 控制台变量：启用快速共享路径

	/**
	 * 条件创建复制驱动程序
	 */
	UReplicationDriver* ConditionalCreateReplicationDriver(UNetDriver* ForNetDriver, UWorld* World)
	{
		// 仅为 GameNetDriver 创建
		if (World && ForNetDriver && ForNetDriver->NetDriverName == NAME_GameNetDriver) // 检查世界和网络驱动程序
		{
			const ULyraReplicationGraphSettings* LyraRepGraphSettings = GetDefault<ULyraReplicationGraphSettings>(); // 获取 Lyra 复制图设置

			// 通过开发者设置启用/禁用
			if (LyraRepGraphSettings && LyraRepGraphSettings->bDisableReplicationGraph) // 检查是否禁用复制图
			{
				UE_LOG(LogLyraRepGraph, Display, TEXT("通过 LyraReplicationGraphSettings 禁用复制图。")); // 记录信息
				return nullptr; // 返回空指针
			}

			UE_LOG(LogLyraRepGraph, Display, TEXT("为 %s 在世界 %s 中启用复制图。"), *GetNameSafe(ForNetDriver), *GetPathNameSafe(World)); // 记录信息

			TSubclassOf<ULyraReplicationGraph> GraphClass = LyraRepGraphSettings->DefaultReplicationGraphClass.TryLoadClass<ULyraReplicationGraph>(); // 尝试加载复制图类
			if (GraphClass.Get() == nullptr) // 检查类是否为空
			{
				GraphClass = ULyraReplicationGraph::StaticClass(); // 使用静态类
			}

			ULyraReplicationGraph* LyraReplicationGraph = NewObject<ULyraReplicationGraph>(GetTransientPackage(), GraphClass.Get()); // 创建新的复制图对象
			return LyraReplicationGraph; // 返回复制图
		}

		return nullptr; // 返回空指针
	}
};

// ----------------------------------------------------------------------------------------------------------

/**
 * 构造函数
 */
ULyraReplicationGraph::ULyraReplicationGraph()
{
	if (!UReplicationDriver::CreateReplicationDriverDelegate().IsBound()) // 检查复制驱动程序创建委托是否未绑定
	{
		UReplicationDriver::CreateReplicationDriverDelegate().BindLambda( // 绑定 Lambda 表达式
			[](UNetDriver* ForNetDriver, const FURL& URL, UWorld* World) -> UReplicationDriver* // Lambda 参数
			{
				return Lyra::RepGraph::ConditionalCreateReplicationDriver(ForNetDriver, World); // 返回条件创建的复制驱动程序
			});
	}
}

/**
 * 重置游戏世界状态
 */
void ULyraReplicationGraph::ResetGameWorldState()
{
	Super::ResetGameWorldState(); // 调用父类重置游戏世界状态

	AlwaysRelevantStreamingLevelActors.Empty(); // 清空始终相关流关卡角色

	for (UNetReplicationGraphConnection* ConnManager : Connections) // 遍历连接管理器
	{
		for (UReplicationGraphNode* ConnectionNode : ConnManager->GetConnectionGraphNodes()) // 遍历连接图节点
		{
			if (ULyraReplicationGraphNode_AlwaysRelevant_ForConnection* AlwaysRelevantConnectionNode = Cast<ULyraReplicationGraphNode_AlwaysRelevant_ForConnection>(ConnectionNode)) // 转换为始终相关连接节点
			{
				AlwaysRelevantConnectionNode->ResetGameWorldState(); // 重置游戏世界状态
			}
		}
	}

	for (UNetReplicationGraphConnection* ConnManager : PendingConnections) // 遍历待处理连接管理器
	{
		for (UReplicationGraphNode* ConnectionNode : ConnManager->GetConnectionGraphNodes()) // 遍历连接图节点
		{
			if (ULyraReplicationGraphNode_AlwaysRelevant_ForConnection* AlwaysRelevantConnectionNode = Cast<ULyraReplicationGraphNode_AlwaysRelevant_ForConnection>(ConnectionNode)) // 转换为始终相关连接节点
			{
				AlwaysRelevantConnectionNode->ResetGameWorldState(); // 重置游戏世界状态
			}
		}
	}
}

/**
 * 获取类节点映射
 */
EClassRepNodeMapping ULyraReplicationGraph::GetClassNodeMapping(UClass* Class) const
{
	if (!Class) // 检查类是否为空
	{
		return EClassRepNodeMapping::NotRouted; // 返回未路由
	}
	
	if (const EClassRepNodeMapping* Ptr = ClassRepNodePolicies.FindWithoutClassRecursion(Class)) // 在类复制节点策略中查找
	{
		return *Ptr; // 返回找到的映射
	}
	
	AActor* ActorCDO = Cast<AActor>(Class->GetDefaultObject()); // 获取角色的类默认对象
	if (!ActorCDO || !ActorCDO->GetIsReplicated()) // 检查角色CDO是否存在且被复制
	{
		return EClassRepNodeMapping::NotRouted; // 返回未路由
	}
		
	auto ShouldSpatialize = [](const AActor* CDO) // Lambda：是否应该空间化
	{
		return CDO->GetIsReplicated() && (!(CDO->bAlwaysRelevant || CDO->bOnlyRelevantToOwner || CDO->bNetUseOwnerRelevancy)); // 返回是否被复制且不是始终相关、仅对所有者相关或使用所有者相关性
	};

	auto GetLegacyDebugStr = [](const AActor* CDO) // Lambda：获取传统调试字符串
	{
		return FString::Printf(TEXT("%s [%d/%d/%d]"), *CDO->GetClass()->GetName(), CDO->bAlwaysRelevant, CDO->bOnlyRelevantToOwner, CDO->bNetUseOwnerRelevancy); // 返回格式化的调试字符串
	};

	// 仅当此类与其父类不同时才处理它。没有必要将每个子类显式放入图类映射中
	UClass* SuperClass = Class->GetSuperClass(); // 获取父类
	if (AActor* SuperCDO = Cast<AActor>(SuperClass->GetDefaultObject())) // 获取父类的CDO
	{
		if (SuperCDO->GetIsReplicated() == ActorCDO->GetIsReplicated() // 检查复制状态是否相同
			&& SuperCDO->bAlwaysRelevant == ActorCDO->bAlwaysRelevant // 检查始终相关是否相同
			&& SuperCDO->bOnlyRelevantToOwner == ActorCDO->bOnlyRelevantToOwner // 检查仅对所有者相关是否相同
			&& SuperCDO->bNetUseOwnerRelevancy == ActorCDO->bNetUseOwnerRelevancy // 检查使用所有者相关性是否相同
			)
		{
			return GetClassNodeMapping(SuperClass); // 返回父类的节点映射
		}
	}

	if (ShouldSpatialize(ActorCDO)) // 检查是否应该空间化
	{
		return EClassRepNodeMapping::Spatialize_Dynamic; // 返回动态空间化
	}
	else if (ActorCDO->bAlwaysRelevant && !ActorCDO->bOnlyRelevantToOwner) // 检查是否始终相关且不仅对所有者相关
	{
		return EClassRepNodeMapping::RelevantAllConnections; // 返回对所有连接相关
	}

	return EClassRepNodeMapping::NotRouted; // 返回未路由
}

/**
 * 注册类复制节点映射
 */
void ULyraReplicationGraph::RegisterClassRepNodeMapping(UClass* Class)
{
	EClassRepNodeMapping Mapping = GetClassNodeMapping(Class); // 获取类节点映射
	ClassRepNodePolicies.Set(Class, Mapping); // 设置类复制节点策略
}

/**
 * 初始化类复制信息
 */
void ULyraReplicationGraph::InitClassReplicationInfo(FClassReplicationInfo& Info, UClass* Class, bool Spatialize) const
{
	AActor* CDO = Class->GetDefaultObject<AActor>(); // 获取类默认对象
	if (Spatialize) // 检查是否空间化
	{
		Info.SetCullDistanceSquared(CDO->GetNetCullDistanceSquared()); // 设置剔除距离平方
		UE_LOG(LogLyraRepGraph, Log, TEXT("为 %s 设置剔除距离为 %f (%f)"), *Class->GetName(), Info.GetCullDistanceSquared(), Info.GetCullDistance()); // 记录日志
	}

	Info.ReplicationPeriodFrame = GetReplicationPeriodFrameForFrequency(CDO->GetNetUpdateFrequency()); // 设置复制周期帧

	UClass* NativeClass = Class; // 本地类
	while (!NativeClass->IsNative() && NativeClass->GetSuperClass() && NativeClass->GetSuperClass() != AActor::StaticClass()) // 查找本地类
	{
		NativeClass = NativeClass->GetSuperClass(); // 移动到父类
	}

	UE_LOG(LogLyraRepGraph, Log, TEXT("为 %s (%s) 设置复制周期为 %d 帧 (%.2f)"), *Class->GetName(), *NativeClass->GetName(), Info.ReplicationPeriodFrame, CDO->GetNetUpdateFrequency()); // 记录日志
}

/**
 * 条件初始化类复制信息
 */
bool ULyraReplicationGraph::ConditionalInitClassReplicationInfo(UClass* ReplicatedClass, FClassReplicationInfo& ClassInfo)
{
	if (ExplicitlySetClasses.FindByPredicate([&](const UClass* SetClass) { return ReplicatedClass->IsChildOf(SetClass); }) != nullptr) // 检查是否显式设置
	{
		return false; // 返回 false
	}

	bool ClassIsSpatialized = IsSpatialized(ClassRepNodePolicies.GetChecked(ReplicatedClass)); // 检查类是否空间化
	InitClassReplicationInfo(ClassInfo, ReplicatedClass, ClassIsSpatialized); // 初始化类复制信息
	return true; // 返回 true
}

/**
 * 添加类复制信息
 */
void ULyraReplicationGraph::AddClassRepInfo(UClass* Class, EClassRepNodeMapping Mapping)
{
	if (IsSpatialized(Mapping)) // 检查是否空间化
	{
		if (Class->GetDefaultObject<AActor>()->bAlwaysRelevant) // 检查是否始终相关
		{
			UE_LOG(LogLyraRepGraph, Warning, TEXT("复制的类 %s 是 AlwaysRelevant 但被初始化为空间化节点 (%s)"), *Class->GetName(), *StaticEnum<EClassRepNodeMapping>()->GetNameStringByValue((int64)Mapping)); // 记录警告
		}
	}

	ClassRepNodePolicies.Set(Class, Mapping); // 设置类复制节点策略
}

/**
 * 注册类复制信息
 */
void ULyraReplicationGraph::RegisterClassReplicationInfo(UClass* ReplicatedClass)
{
	FClassReplicationInfo ClassInfo; // 类复制信息
	if (ConditionalInitClassReplicationInfo(ReplicatedClass, ClassInfo)) // 条件初始化类复制信息
	{
		GlobalActorReplicationInfoMap.SetClassInfo(ReplicatedClass, ClassInfo); // 设置全局角色复制信息映射
		UE_LOG(LogLyraRepGraph, Log, TEXT("设置 %s - %.2f"), *GetNameSafe(ReplicatedClass), ClassInfo.GetCullDistance()); // 记录日志
	}
}

/**
 * 初始化全局角色类设置
 */
void ULyraReplicationGraph::InitGlobalActorClassSettings()
{
	// 设置我们的延迟初始化函数，用于当前未加载的类。
	GlobalActorReplicationInfoMap.SetInitClassInfoFunc( // 设置初始化类信息函数
		[this](UClass* Class, FClassReplicationInfo& ClassInfo) // Lambda 参数
		{
			RegisterClassRepNodeMapping(Class); // 这需要在 RegisterClassReplicationInfo 之前运行。

			const bool bHandled = ConditionalInitClassReplicationInfo(Class, ClassInfo); // 条件初始化类复制信息

#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) // 非发布和非测试构建
			if (Lyra::RepGraph::LogLazyInitClasses != 0) // 检查是否记录延迟初始化类
			{
				if (bHandled) // 检查是否已处理
				{
					EClassRepNodeMapping Mapping = ClassRepNodePolicies.GetChecked(Class); // 获取映射
					UE_LOG(LogLyraRepGraph, Warning, TEXT("%s 被延迟初始化。（父级：%s）%d。"), *GetNameSafe(Class), *GetNameSafe(Class->GetSuperClass()), (int32)Mapping); // 记录警告

					FClassReplicationInfo& ParentRepInfo = GlobalActorReplicationInfoMap.GetClassInfo(Class->GetSuperClass()); // 获取父级复制信息
					if (ClassInfo.BuildDebugStringDelta() != ParentRepInfo.BuildDebugStringDelta()) // 检查调试字符串差异
					{
						UE_LOG(LogLyraRepGraph, Warning, TEXT("发现差异！")); // 记录警告
						FString DebugStr = ParentRepInfo.BuildDebugStringDelta(); // 构建父级调试字符串
						UE_LOG(LogLyraRepGraph, Warning, TEXT("  父级：%s"), *DebugStr); // 记录父级调试字符串

						DebugStr = ClassInfo.BuildDebugStringDelta(); // 构建类调试字符串
						UE_LOG(LogLyraRepGraph, Warning, TEXT("  类  ：%s"), *DebugStr); // 记录类调试字符串
					}
				}
				else
				{
					UE_LOG(LogLyraRepGraph, Warning, TEXT("%s 跳过了延迟初始化，因为它与其父级没有不同。（父级：%s）"), *GetNameSafe(Class), *GetNameSafe(Class->GetSuperClass())); // 记录警告

				}
			}
#endif

			return bHandled; // 返回是否已处理
		});

	ClassRepNodePolicies.InitNewElement = [this](UClass* Class, EClassRepNodeMapping& NodeMapping) // 初始化新元素函数
	{
		NodeMapping = GetClassNodeMapping(Class); // 获取类节点映射
		return true; // 返回 true
	};

	const ULyraReplicationGraphSettings* LyraRepGraphSettings = GetDefault<ULyraReplicationGraphSettings>(); // 获取 Lyra 复制图设置
	check(LyraRepGraphSettings); // 检查设置是否有效

	// 设置类节点映射
	for (const FRepGraphActorClassSettings& ActorClassSettings : LyraRepGraphSettings->ClassSettings) // 遍历角色类设置
	{
		if (ActorClassSettings.bAddClassRepInfoToMap) // 检查是否添加到映射
		{
			if (UClass* StaticActorClass = ActorClassSettings.GetStaticActorClass()) // 获取静态角色类
			{
				UE_LOG(LogLyraRepGraph, Log, TEXT("ActorClassSettings -- AddClassRepInfo - %s :: %i"), *StaticActorClass->GetName(), int(ActorClassSettings.ClassNodeMapping)); // 记录日志
				AddClassRepInfo(StaticActorClass, ActorClassSettings.ClassNodeMapping); // 添加类复制信息
			}
		}
	}

#if WITH_GAMEPLAY_DEBUGGER // 如果有游戏调试器
	AddClassRepInfo(AGameplayDebuggerCategoryReplicator::StaticClass(), EClassRepNodeMapping::NotRouted);				// 通过 ULyraReplicationGraphNode_AlwaysRelevant_ForConnection 复制
#endif

	TArray<UClass*> AllReplicatedClasses; // 所有复制类数组

	for (TObjectIterator<UClass> It; It; ++It) // 遍历所有 UClass 对象
	{
		UClass* Class = *It; // 获取类
		AActor* ActorCDO = Cast<AActor>(Class->GetDefaultObject()); // 获取角色类默认对象
		if (!ActorCDO || !ActorCDO->GetIsReplicated()) // 检查角色CDO是否存在且被复制
		{
			continue; // 继续循环
		}

		// 跳过 SKEL 和 REINST 类。我不知道更好的方法。
		if (Class->GetName().StartsWith(TEXT("SKEL_")) || Class->GetName().StartsWith(TEXT("REINST_"))) // 检查是否为 SKEL 或 REINST 类
		{
			continue; // 继续循环
		}

		// --------------------------------------------------------------------
		// 这是一个复制的类。将其保存下来用于下面的第二遍处理
		// --------------------------------------------------------------------

		AllReplicatedClasses.Add(Class); // 添加到所有复制类数组

		RegisterClassRepNodeMapping(Class); // 注册类复制节点映射
	}

	// -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
	// 设置 FClassReplicationInfo。这本质上是每个类的复制设置。有些我们显式设置，其余我们通过查看 AActor 上的传统设置来设置。
	// -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

	auto SetClassInfo = [&](UClass* Class, const FClassReplicationInfo& Info) { GlobalActorReplicationInfoMap.SetClassInfo(Class, Info); ExplicitlySetClasses.Add(Class); }; // Lambda：设置类信息
	ExplicitlySetClasses.Reset(); // 重置显式设置类

	FClassReplicationInfo CharacterClassRepInfo; // 角色类复制信息
	CharacterClassRepInfo.DistancePriorityScale = 1.f; // 距离优先级缩放
	CharacterClassRepInfo.StarvationPriorityScale = 1.f; // 饥饿优先级缩放
	CharacterClassRepInfo.ActorChannelFrameTimeout = 4; // 角色通道帧超时
	CharacterClassRepInfo.SetCullDistanceSquared(ALyraCharacter::StaticClass()->GetDefaultObject<ALyraCharacter>()->GetNetCullDistanceSquared()); // 设置剔除距离平方

	SetClassInfo(ACharacter::StaticClass(), CharacterClassRepInfo); // 设置角色类信息

	{
		// 检查我们的 FSharedRepMovement 类型是否与默认角色具有相同的量化设置。
		FRepMovement DefaultRepMovement = ALyraCharacter::StaticClass()->GetDefaultObject<ALyraCharacter>()->GetReplicatedMovement(); // 使用与我们默认复制移动相同的量化设置
		FSharedRepMovement SharedRepMovement; // 共享复制移动
		ensureMsgf(SharedRepMovement.RepMovement.LocationQuantizationLevel == DefaultRepMovement.LocationQuantizationLevel, TEXT("LocationQuantizationLevel 不匹配。%d != %d"), (uint8)SharedRepMovement.RepMovement.LocationQuantizationLevel, (uint8)DefaultRepMovement.LocationQuantizationLevel); // 确保位置量化级别匹配
		ensureMsgf(SharedRepMovement.RepMovement.VelocityQuantizationLevel == DefaultRepMovement.VelocityQuantizationLevel, TEXT("VelocityQuantizationLevel 不匹配。%d != %d"), (uint8)SharedRepMovement.RepMovement.VelocityQuantizationLevel, (uint8)DefaultRepMovement.VelocityQuantizationLevel); // 确保速度量化级别匹配
		ensureMsgf(SharedRepMovement.RepMovement.RotationQuantizationLevel == DefaultRepMovement.RotationQuantizationLevel, TEXT("RotationQuantizationLevel 不匹配。%d != %d"), (uint8)SharedRepMovement.RepMovement.RotationQuantizationLevel, (uint8)DefaultRepMovement.RotationQuantizationLevel); // 确保旋转量化级别匹配
	}

	// ------------------------------------------------------------------------------------------------------
	//	设置 Pawn 的快速共享复制。这每帧每个 Pawn 最多调用一次，以查看它是否想要
	//	向所有相关连接发送快速共享更新。
	// ------------------------------------------------------------------------------------------------------
	CharacterClassRepInfo.FastSharedReplicationFunc = [](AActor* Actor) // 快速共享复制函数
	{
		bool bSuccess = false; // 成功标志
		if (ALyraCharacter* Character = Cast<ALyraCharacter>(Actor)) // 转换为 Lyra 角色
		{
			bSuccess = Character->UpdateSharedReplication(); // 更新共享复制
		}
		return bSuccess; // 返回成功标志
	};

	CharacterClassRepInfo.FastSharedReplicationFuncName = FName(TEXT("FastSharedReplication")); // 快速共享复制函数名称

	FastSharedPathConstants.MaxBitsPerFrame = (int32)((float)(Lyra::RepGraph::TargetKBytesSecFastSharedPath * 1024 * 8) / NetDriver->GetNetServerMaxTickRate()); // 快速共享路径每帧最大位数
	FastSharedPathConstants.DistanceRequirementPct = Lyra::RepGraph::FastSharedPathCullDistPct; // 快速共享路径距离要求百分比

	SetClassInfo(ALyraCharacter::StaticClass(), CharacterClassRepInfo); // 设置 Lyra 角色类信息

	// ---------------------------------------------------------------------
	UReplicationGraphNode_ActorListFrequencyBuckets::DefaultSettings.ListSize = 12; // 角色列表频率桶默认设置列表大小
	UReplicationGraphNode_ActorListFrequencyBuckets::DefaultSettings.NumBuckets = Lyra::RepGraph::DynamicActorFrequencyBuckets; // 角色列表频率桶默认设置桶数量
	UReplicationGraphNode_ActorListFrequencyBuckets::DefaultSettings.BucketThresholds.Reset(); // 角色列表频率桶默认设置桶阈值重置
	UReplicationGraphNode_ActorListFrequencyBuckets::DefaultSettings.EnableFastPath = (Lyra::RepGraph::EnableFastSharedPath > 0); // 角色列表频率桶默认设置启用快速路径
	UReplicationGraphNode_ActorListFrequencyBuckets::DefaultSettings.FastPathFrameModulo = 1; // 角色列表频率桶默认设置快速路径帧模数

	RPCSendPolicyMap.Reset(); // 重置 RPC 发送策略映射

	// 基于所有复制类的传统设置设置 FClassReplicationInfo
	for (UClass* ReplicatedClass : AllReplicatedClasses) // 遍历所有复制类
	{
		RegisterClassReplicationInfo(ReplicatedClass); // 注册类复制信息
	}

	// 打印我们得出的结果
	UE_LOG(LogLyraRepGraph, Log, TEXT("")); // 记录空行
	UE_LOG(LogLyraRepGraph, Log, TEXT("类路由映射：")); // 记录类路由映射标题
	for (auto ClassMapIt = ClassRepNodePolicies.CreateIterator(); ClassMapIt; ++ClassMapIt) // 遍历类复制节点策略
	{
		UClass* Class = CastChecked<UClass>(ClassMapIt.Key().ResolveObjectPtr()); // 获取类
		EClassRepNodeMapping Mapping = ClassMapIt.Value(); // 获取映射

		// 仅在与本地类不同时打印
		UClass* ParentNativeClass = GetParentNativeClass(Class); // 获取父级本地类

		EClassRepNodeMapping* ParentMapping = ClassRepNodePolicies.Get(ParentNativeClass); // 获取父级映射
		if (ParentMapping && Class != ParentNativeClass && Mapping == *ParentMapping) // 检查是否与父级相同
		{
			continue; // 继续循环
		}

		UE_LOG(LogLyraRepGraph, Log, TEXT("  %s (%s) -> %s"), *Class->GetName(), *GetNameSafe(ParentNativeClass), *StaticEnum<EClassRepNodeMapping>()->GetNameStringByValue((int64)Mapping)); // 记录类路由信息
	}

	UE_LOG(LogLyraRepGraph, Log, TEXT("")); // 记录空行
	UE_LOG(LogLyraRepGraph, Log, TEXT("类设置映射：")); // 记录类设置映射标题
	FClassReplicationInfo DefaultValues; // 默认值
	for (auto ClassRepInfoIt = GlobalActorReplicationInfoMap.CreateClassMapIterator(); ClassRepInfoIt; ++ClassRepInfoIt) // 遍历全局角色复制信息映射
	{
		UClass* Class = CastChecked<UClass>(ClassRepInfoIt.Key().ResolveObjectPtr()); // 获取类
		const FClassReplicationInfo& ClassInfo = ClassRepInfoIt.Value(); // 获取类复制信息
		UE_LOG(LogLyraRepGraph, Log, TEXT("  %s (%s) -> %s"), *Class->GetName(), *GetNameSafe(GetParentNativeClass(Class)), *ClassInfo.BuildDebugStringDelta()); // 记录类设置信息
	}


	// 根据 CVar 值复制销毁信息
	DestructInfoMaxDistanceSquared = Lyra::RepGraph::DestructionInfoMaxDist * Lyra::RepGraph::DestructionInfoMaxDist; // 销毁信息最大距离平方

#if WITH_GAMEPLAY_DEBUGGER // 如果有游戏调试器
	AGameplayDebuggerCategoryReplicator::NotifyDebuggerOwnerChange.AddUObject(this, &ThisClass::OnGameplayDebuggerOwnerChange); // 添加游戏调试器所有者变更通知
#endif

	// 添加到 RPC_Multicast_OpenChannelForClass 映射
	RPC_Multicast_OpenChannelForClass.Reset(); // 重置 RPC 多播打开通道类映射
	RPC_Multicast_OpenChannelForClass.Set(AActor::StaticClass(), true); // 默认情况下为多播 RPC 打开通道
	RPC_Multicast_OpenChannelForClass.Set(AController::StaticClass(), false); // 多播绝不应在控制器上打开通道，因为在非所有者上打开通道会破坏控制器的复制。
	RPC_Multicast_OpenChannelForClass.Set(AServerStatReplicator::StaticClass(), false); // 服务器统计复制器不打开通道

	for (const FRepGraphActorClassSettings& ActorClassSettings : LyraRepGraphSettings->ClassSettings) // 遍历角色类设置
	{
		if (ActorClassSettings.bAddToRPC_Multicast_OpenChannelForClassMap) // 检查是否添加到 RPC 多播打开通道类映射
		{
			if (UClass* StaticActorClass = ActorClassSettings.GetStaticActorClass()) // 获取静态角色类
			{
				UE_LOG(LogLyraRepGraph, Log, TEXT("ActorClassSettings -- RPC_Multicast_OpenChannelForClass - %s"), *StaticActorClass->GetName()); // 记录日志
				RPC_Multicast_OpenChannelForClass.Set(StaticActorClass, ActorClassSettings.bRPC_Multicast_OpenChannelForClass); // 设置 RPC 多播打开通道类映射
			}
		}
	}
}

/**
 * 初始化全局图节点
 */
void ULyraReplicationGraph::InitGlobalGraphNodes()
{
	// -----------------------------------------------
	//	空间化角色
	// -----------------------------------------------

	GridNode = CreateNewNode<UReplicationGraphNode_GridSpatialization2D>(); // 创建新的网格空间化2D节点
	GridNode->CellSize = Lyra::RepGraph::CellSize; // 设置单元格大小
	GridNode->SpatialBias = FVector2D(Lyra::RepGraph::SpatialBiasX, Lyra::RepGraph::SpatialBiasY); // 设置空间偏置

	if (Lyra::RepGraph::DisableSpatialRebuilds) // 检查是否禁用空间重建
	{
		GridNode->AddToClassRebuildDenyList(AActor::StaticClass()); // 禁用所有空间重建
	}
	
	AddGlobalGraphNode(GridNode); // 添加全局图节点

	// -----------------------------------------------
	//	始终相关（对所有人）角色
	// -----------------------------------------------
	AlwaysRelevantNode = CreateNewNode<UReplicationGraphNode_ActorList>(); // 创建新的角色列表节点
	AddGlobalGraphNode(AlwaysRelevantNode); // 添加全局图节点

	// -----------------------------------------------
	//	玩家状态专业化。这将返回一个玩家状态的滚动子集进行复制
	// -----------------------------------------------
	ULyraReplicationGraphNode_PlayerStateFrequencyLimiter* PlayerStateNode = CreateNewNode<ULyraReplicationGraphNode_PlayerStateFrequencyLimiter>(); // 创建新的玩家状态频率限制器节点
	AddGlobalGraphNode(PlayerStateNode); // 添加全局图节点
}

/**
 * 初始化连接图节点
 */
void ULyraReplicationGraph::InitConnectionGraphNodes(UNetReplicationGraphConnection* RepGraphConnection)
{
	Super::InitConnectionGraphNodes(RepGraphConnection); // 调用父类初始化连接图节点

	ULyraReplicationGraphNode_AlwaysRelevant_ForConnection* AlwaysRelevantConnectionNode = CreateNewNode<ULyraReplicationGraphNode_AlwaysRelevant_ForConnection>(); // 创建新的始终相关连接节点

	// 此节点需要知道客户端关卡何时进入和退出可见性
	RepGraphConnection->OnClientVisibleLevelNameAdd.AddUObject(AlwaysRelevantConnectionNode, &ULyraReplicationGraphNode_AlwaysRelevant_ForConnection::OnClientLevelVisibilityAdd); // 添加客户端可见关卡名称添加事件
	RepGraphConnection->OnClientVisibleLevelNameRemove.AddUObject(AlwaysRelevantConnectionNode, &ULyraReplicationGraphNode_AlwaysRelevant_ForConnection::OnClientLevelVisibilityRemove); // 添加客户端可见关卡名称移除事件

	AddConnectionGraphNode(AlwaysRelevantConnectionNode, RepGraphConnection); // 添加连接图节点
}

/**
 * 获取映射策略
 */
EClassRepNodeMapping ULyraReplicationGraph::GetMappingPolicy(UClass* Class)
{
	EClassRepNodeMapping* PolicyPtr = ClassRepNodePolicies.Get(Class); // 获取策略指针
	EClassRepNodeMapping Policy = PolicyPtr ? *PolicyPtr : EClassRepNodeMapping::NotRouted; // 获取策略或未路由
	return Policy; // 返回策略
}

/**
 * 路由添加网络角色到节点
 */
void ULyraReplicationGraph::RouteAddNetworkActorToNodes(const FNewReplicatedActorInfo& ActorInfo, FGlobalActorReplicationInfo& GlobalInfo)
{
	EClassRepNodeMapping Policy = GetMappingPolicy(ActorInfo.Class); // 获取映射策略
	switch(Policy) // 根据策略切换
	{
		case EClassRepNodeMapping::NotRouted: // 未路由
		{
			break; // 中断
		}
		
		case EClassRepNodeMapping::RelevantAllConnections: // 对所有连接相关
		{
			if (ActorInfo.StreamingLevelName == NAME_None) // 检查流关卡名称是否为空
			{
				AlwaysRelevantNode->NotifyAddNetworkActor(ActorInfo); // 通知添加网络角色
			}
			else
			{
				FActorRepListRefView& RepList = AlwaysRelevantStreamingLevelActors.FindOrAdd(ActorInfo.StreamingLevelName); // 查找或添加始终相关流关卡角色
				RepList.ConditionalAdd(ActorInfo.Actor); // 条件添加角色
			}
			break; // 中断
		}

		case EClassRepNodeMapping::Spatialize_Static: // 静态空间化
		{
			GridNode->AddActor_Static(ActorInfo, GlobalInfo); // 添加静态角色
			break; // 中断
		}
		
		case EClassRepNodeMapping::Spatialize_Dynamic: // 动态空间化
		{
			GridNode->AddActor_Dynamic(ActorInfo, GlobalInfo); // 添加动态角色
			break; // 中断
		}
		
		case EClassRepNodeMapping::Spatialize_Dormancy: // 休眠空间化
		{
			GridNode->AddActor_Dormancy(ActorInfo, GlobalInfo); // 添加休眠角色
			break; // 中断
		}
	};
}

/**
 * 路由移除网络角色到节点
 */
void ULyraReplicationGraph::RouteRemoveNetworkActorToNodes(const FNewReplicatedActorInfo& ActorInfo)
{
	EClassRepNodeMapping Policy = GetMappingPolicy(ActorInfo.Class); // 获取映射策略
	switch(Policy) // 根据策略切换
	{
		case EClassRepNodeMapping::NotRouted: // 未路由
		{
			break; // 中断
		}
		
		case EClassRepNodeMapping::RelevantAllConnections: // 对所有连接相关
		{
			if (ActorInfo.StreamingLevelName == NAME_None) // 检查流关卡名称是否为空
			{
				AlwaysRelevantNode->NotifyRemoveNetworkActor(ActorInfo); // 通知移除网络角色
			}
			else
			{
				FActorRepListRefView& RepList = AlwaysRelevantStreamingLevelActors.FindChecked(ActorInfo.StreamingLevelName); // 查找始终相关流关卡角色
				if (RepList.RemoveFast(ActorInfo.Actor) == false) // 快速移除角色
				{
					UE_LOG(LogLyraRepGraph, Warning, TEXT("角色 %s 未在 AlwaysRelevantStreamingLevelActors 列表中找到。关卡名称：%s"), *GetActorRepListTypeDebugString(ActorInfo.Actor), *ActorInfo.StreamingLevelName.ToString()); // 记录警告
				}				
			}

			SetActorDestructionInfoToIgnoreDistanceCulling(ActorInfo.GetActor()); // 设置角色销毁信息以忽略距离剔除

			break; // 中断
		}

		case EClassRepNodeMapping::Spatialize_Static: // 静态空间化
		{
			GridNode->RemoveActor_Static(ActorInfo); // 移除静态角色
			break; // 中断
		}
		
		case EClassRepNodeMapping::Spatialize_Dynamic: // 动态空间化
		{
			GridNode->RemoveActor_Dynamic(ActorInfo); // 移除动态角色
			break; // 中断
		}
		
		case EClassRepNodeMapping::Spatialize_Dormancy: // 休眠空间化
		{
			GridNode->RemoveActor_Dormancy(ActorInfo); // 移除休眠角色
			break; // 中断
		}
	};
}

// 由于我们监听全局（静态）事件，我们需要注意跨世界广播（PIE）
#if WITH_EDITOR // 如果有编辑器
#define CHECK_WORLDS(X) if(X->GetWorld() != GetWorld()) return; // 检查世界宏
#else // 否则
#define CHECK_WORLDS(X) // 空宏
#endif // 结束如果

#if WITH_GAMEPLAY_DEBUGGER // 如果有游戏调试器
/**
 * 游戏调试器所有者变更处理
 */
void ULyraReplicationGraph::OnGameplayDebuggerOwnerChange(AGameplayDebuggerCategoryReplicator* Debugger, APlayerController* OldOwner)
{
	CHECK_WORLDS(Debugger); // 检查世界

	auto GetAlwaysRelevantForConnectionNode = [this](APlayerController* Controller) -> ULyraReplicationGraphNode_AlwaysRelevant_ForConnection* // Lambda：获取始终相关连接节点
	{
		if (Controller) // 检查控制器
		{
			if (UNetConnection* NetConnection = Controller->GetNetConnection()) // 获取网络连接
			{
				if (NetConnection->GetDriver() == NetDriver) // 检查网络驱动程序
				{
					if (UNetReplicationGraphConnection* GraphConnection = FindOrAddConnectionManager(NetConnection)) // 查找或添加连接管理器
					{
						for (UReplicationGraphNode* ConnectionNode : GraphConnection->GetConnectionGraphNodes()) // 遍历连接图节点
						{
							if (ULyraReplicationGraphNode_AlwaysRelevant_ForConnection* AlwaysRelevantConnectionNode = Cast<ULyraReplicationGraphNode_AlwaysRelevant_ForConnection>(ConnectionNode)) // 转换为始终相关连接节点
							{
								return AlwaysRelevantConnectionNode; // 返回始终相关连接节点
							}
						}
					}
				}
			}
		}

		return nullptr; // 返回空指针
	};

	if (ULyraReplicationGraphNode_AlwaysRelevant_ForConnection* AlwaysRelevantConnectionNode = GetAlwaysRelevantForConnectionNode(OldOwner)) // 获取旧所有者的始终相关连接节点
	{
		AlwaysRelevantConnectionNode->GameplayDebugger = nullptr; // 设置游戏调试器为空
	}

	if (ULyraReplicationGraphNode_AlwaysRelevant_ForConnection* AlwaysRelevantConnectionNode = GetAlwaysRelevantForConnectionNode(Debugger->GetReplicationOwner())) // 获取调试器复制所有者的始终相关连接节点
	{
		AlwaysRelevantConnectionNode->GameplayDebugger = Debugger; // 设置游戏调试器
	}
}
#endif // 结束如果有游戏调试器

#undef CHECK_WORLDS // 取消定义检查世界宏

// ------------------------------------------------------------------------------

/**
 * 重置游戏世界状态
 */
void ULyraReplicationGraphNode_AlwaysRelevant_ForConnection::ResetGameWorldState()
{
	ReplicationActorList.Reset(); // 重置复制角色列表
	AlwaysRelevantStreamingLevelsNeedingReplication.Empty(); // 清空需要复制的始终相关流关卡
}

/**
 * 为连接收集角色列表
 */
void ULyraReplicationGraphNode_AlwaysRelevant_ForConnection::GatherActorListsForConnection(const FConnectionGatherActorListParameters& Params)
{
	ULyraReplicationGraph* LyraGraph = CastChecked<ULyraReplicationGraph>(GetOuter()); // 获取 Lyra 复制图

	ReplicationActorList.Reset(); // 重置复制角色列表

	for (const FNetViewer& CurViewer : Params.Viewers) // 遍历网络查看器
	{
		ReplicationActorList.ConditionalAdd(CurViewer.InViewer); // 条件添加查看者
		ReplicationActorList.ConditionalAdd(CurViewer.ViewTarget); // 条件添加视图目标

		if (ALyraPlayerController* PC = Cast<ALyraPlayerController>(CurViewer.InViewer)) // 转换为 Lyra 玩家控制器
		{
			// 50% 的玩家状态节流。
			const bool bReplicatePS = (Params.ConnectionManager.ConnectionOrderNum % 2) == (Params.ReplicationFrameNum % 2); // 检查是否复制玩家状态
			if (bReplicatePS) // 如果复制玩家状态
			{
				// 始终将玩家状态返回给拥有玩家。模拟代理玩家状态由 ULyraReplicationGraphNode_PlayerStateFrequencyLimiter 处理
				if (APlayerState* PS = PC->PlayerState) // 获取玩家状态
				{
					if (!bInitializedPlayerState) // 检查玩家状态是否未初始化
					{
						bInitializedPlayerState = true; // 设置玩家状态已初始化
						FConnectionReplicationActorInfo& ConnectionActorInfo = Params.ConnectionManager.ActorInfoMap.FindOrAdd(PS); // 查找或添加连接角色信息
						ConnectionActorInfo.ReplicationPeriodFrame = 1; // 设置复制周期帧
					}

					ReplicationActorList.ConditionalAdd(PS); // 条件添加玩家状态
				}
			}

			FCachedAlwaysRelevantActorInfo& LastData = PastRelevantActorMap.FindOrAdd(CurViewer.Connection); // 查找或添加缓存的始终相关角色信息

			if (ALyraCharacter* Pawn = Cast<ALyraCharacter>(PC->GetPawn())) // 获取 Pawn
			{
				UpdateCachedRelevantActor(Params, Pawn, LastData.LastViewer); // 更新缓存的相前角色

				if (Pawn != CurViewer.ViewTarget) // 检查 Pawn 是否不是视图目标
				{
					ReplicationActorList.ConditionalAdd(Pawn); // 条件添加 Pawn
				}
			}

			if (ALyraCharacter* ViewTargetPawn = Cast<ALyraCharacter>(CurViewer.ViewTarget)) // 获取视图目标 Pawn
			{
				UpdateCachedRelevantActor(Params, ViewTargetPawn, LastData.LastViewTarget); // 更新缓存的视图目标角色
			}
		}
	}

	CleanupCachedRelevantActors(PastRelevantActorMap); // 清理缓存的相前角色

	// 始终相关流关卡角色。
	FPerConnectionActorInfoMap& ConnectionActorInfoMap = Params.ConnectionManager.ActorInfoMap; // 连接角色信息映射
	
	TMap<FName, FActorRepListRefView>& AlwaysRelevantStreamingLevelActors = LyraGraph->AlwaysRelevantStreamingLevelActors; // 始终相关流关卡角色

	for (int32 Idx=AlwaysRelevantStreamingLevelsNeedingReplication.Num()-1; Idx >= 0; --Idx) // 遍历需要复制的始终相关流关卡
	{
		const FName& StreamingLevel = AlwaysRelevantStreamingLevelsNeedingReplication[Idx]; // 获取流关卡

		FActorRepListRefView* Ptr = AlwaysRelevantStreamingLevelActors.Find(StreamingLevel); // 查找流关卡角色列表
		if (Ptr == nullptr) // 检查指针是否为空
		{
			// 该关卡没有始终相关列表
			UE_CLOG(Lyra::RepGraph::DisplayClientLevelStreaming > 0, LogLyraRepGraph, Display, TEXT("CLIENTSTREAMING 从 AlwaysRelevantStreamingLevelActors 中移除 %s，因为 FActorRepListRefView 为空。%s "), *StreamingLevel.ToString(),  *Params.ConnectionManager.GetName()); // 条件记录显示
			AlwaysRelevantStreamingLevelsNeedingReplication.RemoveAtSwap(Idx, EAllowShrinking::No); // 移除需要复制的流关卡
			continue; // 继续循环
		}

		FActorRepListRefView& RepList = *Ptr; // 获取复制列表

		if (RepList.Num() > 0) // 检查复制列表是否不为空
		{
			bool bAllDormant = true; // 全部休眠标志
			for (FActorRepListType Actor : RepList) // 遍历复制列表
			{
				FConnectionReplicationActorInfo& ConnectionActorInfo = ConnectionActorInfoMap.FindOrAdd(Actor); // 查找或添加连接角色信息
				if (ConnectionActorInfo.bDormantOnConnection == false) // 检查连接上是否不休眠
				{
					bAllDormant = false; // 设置全部休眠标志为 false
					break; // 中断循环
				}
			}

			if (bAllDormant) // 如果全部休眠
			{
				UE_CLOG(Lyra::RepGraph::DisplayClientLevelStreaming > 0, LogLyraRepGraph, Display, TEXT("CLIENTSTREAMING 流关卡 %s 上的所有始终相关角色在 %s 上休眠。移除列表。"), *StreamingLevel.ToString(), *Params.ConnectionManager.GetName()); // 条件记录显示
				AlwaysRelevantStreamingLevelsNeedingReplication.RemoveAtSwap(Idx, EAllowShrinking::No); // 移除需要复制的流关卡
			}
			else
			{
				UE_CLOG(Lyra::RepGraph::DisplayClientLevelStreaming > 0, LogLyraRepGraph, Display, TEXT("CLIENTSTREAMING 添加流关卡 %s 上的始终相关角色到 %s，因为它至少有一个不休眠的角色"), *StreamingLevel.ToString(), *Params.ConnectionManager.GetName()); // 条件记录显示
				Params.OutGatheredReplicationLists.AddReplicationActorList(RepList); // 添加复制角色列表
			}
		}
		else
		{
			UE_LOG(LogLyraRepGraph, Warning, TEXT("ULyraReplicationGraphNode_AlwaysRelevant_ForConnection::GatherActorListsForConnection - 空 RepList %s"), *Params.ConnectionManager.GetName()); // 记录警告
		}

	}

#if WITH_GAMEPLAY_DEBUGGER // 如果有游戏调试器
	if (GameplayDebugger) // 检查游戏调试器
	{
		ReplicationActorList.ConditionalAdd(GameplayDebugger); // 条件添加游戏调试器
	}
#endif

	Params.OutGatheredReplicationLists.AddReplicationActorList(ReplicationActorList); // 添加复制角色列表
}

/**
 * 客户端关卡可见性添加
 */
void ULyraReplicationGraphNode_AlwaysRelevant_ForConnection::OnClientLevelVisibilityAdd(FName LevelName, UWorld* StreamingWorld)
{
	UE_CLOG(Lyra::RepGraph::DisplayClientLevelStreaming > 0, LogLyraRepGraph, Display, TEXT("CLIENTSTREAMING ::OnClientLevelVisibilityAdd - %s"), *LevelName.ToString()); // 条件记录显示
	AlwaysRelevantStreamingLevelsNeedingReplication.Add(LevelName); // 添加需要复制的始终相关流关卡
}

/**
 * 客户端关卡可见性移除
 */
void ULyraReplicationGraphNode_AlwaysRelevant_ForConnection::OnClientLevelVisibilityRemove(FName LevelName)
{
	UE_CLOG(Lyra::RepGraph::DisplayClientLevelStreaming > 0, LogLyraRepGraph, Display, TEXT("CLIENTSTREAMING ::OnClientLevelVisibilityRemove - %s"), *LevelName.ToString()); // 条件记录显示
	AlwaysRelevantStreamingLevelsNeedingReplication.Remove(LevelName); // 移除需要复制的始终相关流关卡
}

/**
 * 记录节点
 */
void ULyraReplicationGraphNode_AlwaysRelevant_ForConnection::LogNode(FReplicationGraphDebugInfo& DebugInfo, const FString& NodeName) const
{
	DebugInfo.Log(NodeName); // 记录节点名称
	DebugInfo.PushIndent(); // 推送缩进
	LogActorRepList(DebugInfo, NodeName, ReplicationActorList); // 记录角色复制列表

	for (const FName& LevelName : AlwaysRelevantStreamingLevelsNeedingReplication) // 遍历需要复制的始终相关流关卡
	{
		ULyraReplicationGraph* LyraGraph = CastChecked<ULyraReplicationGraph>(GetOuter()); // 获取 Lyra 复制图
		if (FActorRepListRefView* RepList = LyraGraph->AlwaysRelevantStreamingLevelActors.Find(LevelName)) // 查找流关卡角色列表
		{
			LogActorRepList(DebugInfo, FString::Printf(TEXT("始终相关流关卡列表：%s"), *LevelName.ToString()), *RepList); // 记录角色复制列表
		}
	}

	DebugInfo.PopIndent(); // 弹出缩进
}

// ------------------------------------------------------------------------------

/**
 * 构造函数
 */
ULyraReplicationGraphNode_PlayerStateFrequencyLimiter::ULyraReplicationGraphNode_PlayerStateFrequencyLimiter()
{
	bRequiresPrepareForReplicationCall = true; // 需要准备复制调用
}

/**
 * 准备复制
 */
void ULyraReplicationGraphNode_PlayerStateFrequencyLimiter::PrepareForReplication()
{
	ReplicationActorLists.Reset(); // 重置复制角色列表
	ForceNetUpdateReplicationActorList.Reset(); // 重置强制网络更新复制角色列表

	ReplicationActorLists.AddDefaulted(); // 添加默认复制角色列表
	FActorRepListRefView* CurrentList = &ReplicationActorLists[0]; // 当前列表

	// 我们每帧重新构建玩家状态列表。这不是最有效的方式，但这是处理玩家断开连接和保持列表紧凑的最简单方式。
	// 如果列表是持久的，我们需要在玩家离开时进行碎片整理。

	for (TActorIterator<APlayerState> It(GetWorld()); It; ++It) // 遍历玩家状态
	{
		APlayerState* PS = *It; // 获取玩家状态
		if (IsActorValidForReplicationGather(PS) == false) // 检查角色是否有效用于复制收集
		{
			continue; // 继续循环
		}

		if (CurrentList->Num() >= TargetActorsPerFrame) // 检查当前列表是否达到每帧目标角色数
		{
			ReplicationActorLists.AddDefaulted(); // 添加默认复制角色列表
			CurrentList = &ReplicationActorLists.Last();  // 当前列表指向最后一个列表
		}
		
		CurrentList->Add(PS); // 添加玩家状态
	}	
}

/**
 * 为连接收集角色列表
 */
void ULyraReplicationGraphNode_PlayerStateFrequencyLimiter::GatherActorListsForConnection(const FConnectionGatherActorListParameters& Params)
{
	const int32 ListIdx = Params.ReplicationFrameNum % ReplicationActorLists.Num(); // 计算列表索引
	Params.OutGatheredReplicationLists.AddReplicationActorList(ReplicationActorLists[ListIdx]); // 添加复制角色列表

	if (ForceNetUpdateReplicationActorList.Num() > 0) // 检查强制网络更新复制角色列表是否不为空
	{
		Params.OutGatheredReplicationLists.AddReplicationActorList(ForceNetUpdateReplicationActorList); // 添加强制网络更新复制角色列表
	}	
}

/**
 * 记录节点
 */
void ULyraReplicationGraphNode_PlayerStateFrequencyLimiter::LogNode(FReplicationGraphDebugInfo& DebugInfo, const FString& NodeName) const
{
	DebugInfo.Log(NodeName); // 记录节点名称
	DebugInfo.PushIndent();	 // 推送缩进

	int32 i=0; // 索引
	for (const FActorRepListRefView& List : ReplicationActorLists) // 遍历复制角色列表
	{
		LogActorRepList(DebugInfo, FString::Printf(TEXT("桶[%d]"), i++), List); // 记录角色复制列表
	}

	DebugInfo.PopIndent(); // 弹出缩进
}

// ------------------------------------------------------------------------------

/**
 * 打印复制节点策略
 */
void ULyraReplicationGraph::PrintRepNodePolicies()
{
	UEnum* Enum = StaticEnum<EClassRepNodeMapping>(); // 获取枚举
	if (!Enum) // 检查枚举是否为空
	{
		return; // 返回
	}

	GLog->Logf(TEXT("====================================")); // 记录分隔符
	GLog->Logf(TEXT("Lyra 复制路由策略")); // 记录标题
	GLog->Logf(TEXT("====================================")); // 记录分隔符

	for (auto It = ClassRepNodePolicies.CreateIterator(); It; ++It) // 遍历类复制节点策略
	{
		FObjectKey ObjKey = It.Key(); // 获取对象键
		
		EClassRepNodeMapping Mapping = It.Value(); // 获取映射

		GLog->Logf(TEXT("%-40s --> %s"), *GetNameSafe(ObjKey.ResolveObjectPtr()), *Enum->GetNameStringByValue(static_cast<uint32>(Mapping))); // 记录类路由信息
	}
}

/**
 * Lyra 打印复制节点策略命令
 */
FAutoConsoleCommandWithWorldAndArgs LyraPrintRepNodePoliciesCmd(TEXT("Lyra.RepGraph.PrintRouting"),TEXT("打印角色类如何被路由到 RepGraph 节点"),
	FConsoleCommandWithWorldAndArgsDelegate::CreateLambda([](const TArray<FString>& Args, UWorld* World) // Lambda 表达式
	{
		for (TObjectIterator<ULyraReplicationGraph> It; It; ++It) // 遍历 Lyra 复制图
		{
			It->PrintRepNodePolicies(); // 打印复制节点策略
		}
	})
);

// ------------------------------------------------------------------------------

/**
 * 更改频率桶命令
 */
FAutoConsoleCommandWithWorldAndArgs ChangeFrequencyBucketsCmd(TEXT("Lyra.RepGraph.FrequencyBuckets"), TEXT("重置频率桶计数。"), FConsoleCommandWithWorldAndArgsDelegate::CreateLambda([](const TArray< FString >& Args, UWorld* World)  // Lambda 表达式
{
	int32 Buckets = 1; // 桶数
	if (Args.Num() > 0) // 检查参数数量
	{
		LexTryParseString<int32>(Buckets, *Args[0]); // 尝试解析字符串为整数
	}

	UE_LOG(LogLyraRepGraph, Display, TEXT("设置频率桶为 %d"), Buckets); // 记录显示
	for (TObjectIterator<UReplicationGraphNode_ActorListFrequencyBuckets> It; It; ++It) // 遍历角色列表频率桶节点
	{
		UReplicationGraphNode_ActorListFrequencyBuckets* Node = *It; // 获取节点
		Node->SetNonStreamingCollectionSize(Buckets); // 设置非流集合大小
	}
}));

